// 该文件中的内容为新系统使用的axios请求接口

import { RSATester, AesTester, RSACryptoTool, AESCryptoTool } from "@/tools/crypto";
import { ClearnEmpryProp, } from "../str";
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from "axios";
// import { pk } from '../crypto/spk'
import {
  PassCodeList,
  AuthErrorCode,
  whiteList,
  GETPKURL,
  SERVER_PUBLIKEY_TIMEOUTCODE,
} from './config'
import { COMMON_SERVER } from './common_server'
import { StandardResponse } from "@/api";
const envObj = import.meta.env
const axiosDefaultConfig: AxiosRequestConfig = {
  baseURL: envObj.VITE_SERVER_API_BASE,
  timeout: parseInt(envObj.VITE_SERVER_TIMEOUT),
}

// ___________________________________________________ rsa加密测试工具部分 ___________________________________________________
// const rsaTester = new RSATester()
// rsaTester.begin()
// .then((t) => t.GenerateTest())
// .then((t) => t.ImportKeyTest())
// .then((t) => t.CiphertextDecodeTest())
// .then((t) => t.verifyAndSign())
// .then((t) => t.toolWebGenerKeyTestImport())
// .then((t) => t.ServerPriviteKeyDecodeServerCiphertext())
// ___________________________________________________ rsa加密测试工具部分 ___________________________________________________
//___________________________________________________ aes加密工具测试 ___________________________________________________
// const aesTester = new AesTester()
// aesTester.begin()
// .then(d=>d.generateTest())
// .then(d=>d.ImportTest())
// .then(d=>d.CiTextDecodeTest())
// .then(d=>d.serverCiphertextTest())
// ___________________________________________________ aes加密工具测试 ___________________________________________________


type PubkResp = { ciphertext: string, sign: string }
type CiphertextReq = { appId?: string, sign?: string, content: string }
// 服务器aes密钥异步契约对象
let fetchingPubkeyPromise: Promise<PubkResp> | null = null
let pubkInited: boolean
// const serverRsaPuk = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAudc1lfbldbQTSpy1slQFL/Di5+wADyBmKsMxVhNemmg4M/xNEbrJXdaNI1WC4oy/OFYSGlg0MOOjwgFii++uKQ8wkjMDIAH4TAy/REazemq9zxt8JzZoJg2Bh0edUkN9bHF1DkJyTvLKLWQwaimW2GvakWcrsZrJuMmv2zxJW1C6eMNyXmbF+KKwmB0eWB/itZR6skQ2svip3hySHpS/RTXrqBWwc8Fvh9Vunm9DnAXV9dYEOrvCKMX8p4OoYk9e4wbrEKvjMFFHfwgZdOL07tuQK8YMP0zBfyFLWV6t9Ii3OpXJlLGQxErNIEWEXR1EhaLP/akCnGnDnGwoureFZQIDAQAB'
// const serverRsaPuk = pk
const CRYPTO_SERVER: AxiosInstance = axios.create(axiosDefaultConfig)


const rsa = new RSACryptoTool()
const aes = new AESCryptoTool()
/**
 * 串行化处理,初始化时,可能同时进入多个请求,此时公钥可能并未返回
 * 也可以可以在顶层业务处理,即main.ts/js处理
 * 即等main.js/ts初始化公钥后,响应业务页面,这么做会导致页面逻辑更加复杂,以及引用混乱问题,难以定位错误
 * @param cfg 
 * @returns 
 */
async function serializationHenlde<I extends InternalAxiosRequestConfig<any> | AxiosRequestConfig<any>>(cfg: I): Promise<I> {
  const { data } = cfg
  if (fetchingPubkeyPromise) {
    const pubkresp = await fetchingPubkeyPromise
    pubkInited = true
    fetchingPubkeyPromise = null
    const pubk = rsa.decrypto(pubkresp.ciphertext)
    if (envObj.DEV) {
      console.log('development environment pubk:', pubk);
    }

    aes.updateKey(pubk)
    pubkInited = true
  }
  const clearnData = ClearnEmpryProp(data || {})
  // 白名单无需处理
  if (whiteList.includes(cfg.url!)) return cfg
  const ciphertext: CiphertextReq = { content: aes.encrypto(clearnData, envObj.VITE_ENABLE_API_APPEND_TIMESTAMP === 'ENABLE') }
  // 加密
  cfg.data = ciphertext

  return cfg
}
/**
 * 发送服务器请求前,在请求头中添加token
 */
CRYPTO_SERVER.interceptors.request.use(async config => {
  const { data, url, method } = config
  if (envObj.DEV) {
    console.log(method, url, data);
  }

  config.headers.token = sessionStorage.getItem(envObj.VITE_TOKEN_KEY)


  if (data instanceof FormData) { return config }
  // 以下为解决公钥初始化问题方案
  // 公钥未初始化完成,且没有正在请求公钥
  if (!pubkInited && !fetchingPubkeyPromise) {
    await rsa.Init()
    fetchingPubkeyPromise = COMMON_SERVER.post<any, PubkResp>(GETPKURL, { puKey: rsa.pubk })
    return serializationHenlde(config)
  } else {
    return serializationHenlde(config)
  }
}, error => {
  return Promise.reject(error)
})
function getTryJson(c?: string) {
  if (!c) return c
  try {
    return JSON.parse(c)
  } catch (e) {
    console.error('json解析异常', e);
    console.info(c)
    return c
  }
}
CRYPTO_SERVER.interceptors.response.use(async (response: AxiosResponse<StandardResponse<any>>) => {
  const { code, message, data } = response.data
  // 二进制文件直接返回
  if (response.request.responseType === 'blob') {
    return response
  }
  // 出现一下代码需要重新登录
  if (AuthErrorCode.includes(code)) {
    sessionStorage.clear()
    window.location.href = `${location.protocol}//${location.host}${envObj.BASE_URL}login`
    return Promise.reject(message)
  }
  // 服务器公钥过期
  const serKeyExpire = SERVER_PUBLIKEY_TIMEOUTCODE.includes(code)
  if (serKeyExpire) {
    // 先解密后在赋值回去,因为串行化方法会使用新的公钥进行加密再次传输给服务器
    response.config.data = aes.decrypto(response.config.data)
    // 服务器公钥过期,更新并重新发送请求,会出现一层递归
    if (!fetchingPubkeyPromise) {
      pubkInited = false
      fetchingPubkeyPromise = COMMON_SERVER.post<any, PubkResp>(GETPKURL, { puKey: rsa.pubk })
      console.error('公钥过期,已处理并重新发起请求...', Date.now());
      return serializationHenlde(response.config).then(cf => CRYPTO_SERVER(cf))
    } else {
      return serializationHenlde(response.config).then(cf => CRYPTO_SERVER(cf))
    }
  }
  // 出现异常
  if (!PassCodeList.includes(code)) return Promise.reject(message)
  // 白名单不需要加解密
  if (whiteList.includes(response.config.url!)) return response
  if (!data) return response
  //  解密
  const responsedataPlaintext = aes.decrypto(data)
  const decodedObj = getTryJson(responsedataPlaintext)
  if (envObj.DEV) {
    console.log(response.config.method, response.config.url, decodedObj);
  }
  response.data = decodedObj
  return response

}, (error) => {
  console.error(error);
  const { response } = error
  // 从拦截器过来的异常消息,可以直接返回
  if (!response) return Promise.reject(error)
  // http异常消息,需要处理
  const { data, status } = response
  if ((status).toString().startsWith('5')) return Promise.reject(data?.message || '服务器出错了QAQ~')
  return Promise.reject(error)
})
export { CRYPTO_SERVER }